﻿/*
 * Copyright (C) 2012, Jesus Requejo <jesus.requejo@gmail.com> 
 * Licensed under Microsoft Public License (Ms-PL)
 * http://www.microsoft.com/en-us/openness/licenses.aspx#MPL
*/

namespace LoaderServiceManager.AppLogic {

   using System;
   using System.Collections.Generic;
   using System.IO;
   using System.Linq;
   using Infrastructure;

   public enum ValidationCategories {
      Name,
      Path,
      Accessibility
   }

   public class ValidationResult {
      public ValidationResult(bool isValid, IDictionary<ValidationCategories, string> errors) {
         IsValid = isValid;
         Errors = errors;
      }

      public bool IsValid { get; private set; }
      public IDictionary<ValidationCategories, string> Errors { get; private set; }
   }

   public class ApplicationServices {

      private const int MaxNameLength = 60;
      private IStorageStrategy m_storageStrategy = new FileSystemStorage();

      public IStorageStrategy StorageStrategy { get { return m_storageStrategy; } }

      public ValidationResult ValidateApplication(string name, string path) {
         bool validName = ValidateName(name);
         bool validPath = ValidatePath(path);
         bool accesible = ValidateAccessibility(path);

         if (validName && validPath && accesible) {
            return new ValidationResult(true, null);
         }

         IDictionary<ValidationCategories, string> errors = new Dictionary<ValidationCategories, string>();
         if (!validName) {
            errors[ValidationCategories.Name] = 
               string.Format("Invalid name. Names can't be empty, can't contain any commas, and their length can't exceed {0} characters.", MaxNameLength);
         }
         if (!validPath) {
            errors[ValidationCategories.Path] = 
               string.Format("The specified path does not seem to correspond to a valid Windows executable or shortcut.");
         }
         if (!accesible) {
            errors[ValidationCategories.Accessibility] = 
               string.Format("File is not accessible.");
         }
         return new ValidationResult(false, errors);
      }

      private bool ValidateAccessibility(string path) {
         var success = false;
         using (System.IO.FileStream fs = FileUtils.TryOpen(path)) {
            success = fs != null ? true : false;
         }
         return success;
      }

      private bool ValidatePath(string path) {
         try {
            return FileUtils.AppearsToBeValidExecutable(path);
         }
         catch (ArgumentException) {
            return false;
         }         
      }

      private bool ValidateName(string name) {
         if (string.IsNullOrWhiteSpace(name)) {
            return false;
         }
         if (name.Length > MaxNameLength) {
            return false;
         }
         if (name.Contains(',')) {
            return false;
         }
         return true;
      }

      public void AddNewApplication(string name, string path, bool enabled, bool launchAtLogon) {
         var app = new ApplicationEntry(null, name, path, enabled, launchAtLogon);
         var appList = StorageStrategy.LoadApplicationsList();
         appList.Add(app);
         StorageStrategy.SaveApplicationsList(appList);
      }

      public void UpdateApplication(int id, string name, string path, bool enabled, bool launchAtLogon) {
         var app = new ApplicationEntry(null, name, path, enabled, launchAtLogon);
         var appList = StorageStrategy.LoadApplicationsList();
         appList[id] = app;
         StorageStrategy.SaveApplicationsList(appList);
      }

      public IList<ApplicationEntry> LoadApplicationList() {
         return StorageStrategy.LoadApplicationsList();
      }

      public void RemoveApplication(int appId) {
         var appList = StorageStrategy.LoadApplicationsList();
         appList.RemoveAt(appId);
         StorageStrategy.SaveApplicationsList(appList);
      }

      public void RunProcess(string appPath) {
         Infrastructure.ProcessUtils.RunProcess(appPath);
      }

      public PermissionsConfiguration GetPermissionsConfiguration() {
         try {
            var permissionsStorage = new PermissionsFileSystemStorage();
            var configuration = permissionsStorage.ReadPermissionsConfiguration();
            return configuration;
         }
         catch (System.IO.FileNotFoundException) {
            // attempt to access configuration file for the first time
            var defaultPermissions = PermissionsChecker.CreateDefaultPermissions();
            SavePermissions(defaultPermissions);
            return defaultPermissions;
         }
      }

      public IEnumerable<WindowsUser> GetWindowsUsers() {
         return WindowsUsersUtils.GetAllInteractiveUsers();
      }

      public void UpdateStartupPermissions(StartupPermissions startupPermissions, IList<string> users, PermissionsConfiguration permissions) {
         permissions.StartupPermissions = startupPermissions;
         permissions.AuthorizedStartupUsers = users.Select(name => new WindowsUser(name, "User")).ToList();
         SavePermissions(permissions);
      }

      public void UpdateServiceManagerPermissions(ServiceManagerPermissions serviceManagerPermissions, IList<string> users, PermissionsConfiguration permissions) {
         permissions.ServiceManagerPermissions = serviceManagerPermissions;
         permissions.AuthorizedServiceManagerUsers = users.Select(name => new WindowsUser(name, "User")).ToList();
         SavePermissions(permissions);
      }

      private void SavePermissions(PermissionsConfiguration permissions) {
         var permissionsStorage = new PermissionsFileSystemStorage();
         permissionsStorage.SavePermissionsConfiguration(permissions);
      }

      internal IList<ApplicationEntry> LoadPredefinedApps() {
         return new List<ApplicationEntry>() {
            CreateAppEntry("Command Prompt", "cmd.exe"),
            CreateAppEntry("Event Viewer", "eventvwr.exe"),
            CreateAppEntry("Registry Editor", "regedit.exe"),
            CreateAppEntry("Task Manager", "taskmgr.exe"),
            CreateAppEntry("Performance Monitor", "perfmon.exe"),
            CreateAppEntry("User Accounts", "netplwiz.exe"),
            CreateAppEntry("Disk Cleanup", "cleanmgr.exe"),
            CreateAppEntry("System Configuration", "msconfig.exe"),
            CreateAppEntry("Computer Management", "compmgmt.msc")
         };
      }

      private ApplicationEntry CreateAppEntry(string appName, string executable) {
         return new ApplicationEntry() { Name = appName, Path = executable };
      }

      private string GetSystemPath(string exe) {
         var system = Environment.SpecialFolder.System;
         var systemPath = Environment.GetFolderPath(system);
         return Path.Combine(systemPath, exe);
      }
   }
}
